home *** CD-ROM | disk | FTP | other *** search
- #!/usr/bin/perl
- #
- # Check the consistency of all init.d scripts in the archive. Run
- # this on bellini.debian.org.
-
- use warnings;
- use strict;
- use File::Basename;
-
- my $warn = 1;
-
- my $basedir = "/org/lintian.debian.org/laboratory/binary";
-
- my @scripts = @ARGV;
- @scripts = <$basedir/*/init.d/*> unless (@scripts);
-
- my %scriptinfo;
- my %provides;
-
- my @virts = qw($local_fs $remote_fs $syslog $time $named
- $portmap $network $all
- $mail-transport-agent $x-font-server
- $null $x-display-manager
- );
- my @harddepheaders = qw(required-start required-stop);
- my @softdepheaders = qw(should-start
- should-stop x-start-before x-stop-after);
- my $lsbheaders = "Provides|Required-Start|Required-Stop|Default-Start|Default-Stop";
- my $optheaders = "x-start-before|x-stop-after|should-start|should-stop";
-
- for my $virt (@virts) {
- $provides{$virt} = ['insserv/etc/insserv.conf'];
- }
-
- # Ignore obsolete scripts, as these are unlikely to cause problems.
- for my $old (qw(glibc evms raid2 ldm sdm)) {
- $provides{$old} = ['obsolete'];
- }
-
- # First pass to load the database
- for my $initdscript (@scripts) {
- next if $initdscript =~ m%/rc|/rcS|/README%;
- my %lsbinfo = parse_lsb_header($initdscript);
- $scriptinfo{$initdscript} = \%lsbinfo;
- next unless ($lsbinfo{'found'});
-
- my %checked;
- for my $provide (split(/[ ,\t]+/, $lsbinfo{provides})) {
- if (exists $provides{$provide}) {
- push(@{$provides{$provide}}, $initdscript)
- } else {
- $provides{$provide} = [$initdscript];
- }
- $checked{$provide} = 1;
- }
- }
-
- for my $provide (sort keys %provides) {
- if (1 < scalar @{$provides{$provide}}) {
- my %script;
- map { $script{basename($_)} = 1; } @{$provides{$provide}};
- if (1 < scalar keys %script) {
- error(sprintf("scripts %s provide duplicate '%s'",
- join(",", short_name(@{$provides{$provide}})),
- $provide));
- }
- }
- }
-
- # Second pass, to see which dependencies are missing
- for my $initdscript (@scripts) {
- next unless ($scriptinfo{$initdscript}->{'found'});
- my $short = short_name($initdscript);
- my %checked;
- my @hardmissing = ();
- for my $header (@harddepheaders) {
- my $list = $scriptinfo{$initdscript}->{$header};
- next unless defined $list;
- for my $facility (split(/[ ,\t]+/, $list)) {
- next if exists $checked{$facility};
- $checked{$facility} = 1;
- push(@hardmissing, $facility)
- unless exists $provides{$facility};
- }
- }
- error("script $short depend on non-existing provides: "
- . join(" ", @hardmissing)) if (@hardmissing);
- my @softmissing = ();
- for my $header (@softdepheaders) {
- my $list = $scriptinfo{$initdscript}->{$header};
- next unless defined $list;
- for my $facility (split(/[ ,\t]+/, $list)) {
- next if exists $checked{$facility};
- $checked{$facility} = 1;
- push(@softmissing, $facility)
- unless exists $provides{$facility};
- }
- }
- warning("script $short relate to non-existing provides: "
- . join(" ", @softmissing)) if (@softmissing);
-
- if (exists $checked{'$syslog'}
- && $scriptinfo{$initdscript}->{'default-start'} =~ m/s/i) {
- error("script $short depend on \$syslog and start from rcS.d/");
- }
- if (!exists $checked{'$remote_fs'}
- && !exists $checked{'$syslog'}
- && $scriptinfo{$initdscript}->{'need_remote_fs'}
- && $scriptinfo{$initdscript}->{'default-start'} =~ m/s/i) {
- warning("script $short possibly missing dependency on \$remote_fs");
- } elsif (!exists $checked{'$local_fs'}
- && !exists $checked{'$remote_fs'}
- && !exists $checked{'$syslog'}
- && $scriptinfo{$initdscript}->{'need_local_fs'}
- && $scriptinfo{$initdscript}->{'default-start'} =~ m/s/i) {
- warning("script $short possibly missing dependency on \$local_fs");
- }
-
- if (!exists $checked{'$local_fs'}
- && $scriptinfo{$initdscript}->{'need_syslog'}) {
- warning("script $short possibly missing dependency on \$syslog");
- }
-
- my %provided;
- for my $provide (split(/[ ,\t]+/,
- $scriptinfo{$initdscript}->{provides})) {
- $provided{$provide} = 1;
- if ($provide =~ m/\$/) {
- error("script $short provide virtual facility $provide");
- }
- }
-
- my $basename = basename($initdscript, ".sh");
- info("script $short does not provide its own name")
- unless exists $provided{$basename};
-
- # Detect common problems with runlevel settings.
- my @startrl = sort split(/\s+/, lc($scriptinfo{$initdscript}->{'default-start'}));
- my @stoprl = sort split(/\s+/, lc($scriptinfo{$initdscript}->{'default-stop'}));
-
- unless ( @startrl || @stoprl) {
- error("script $short do not start or stop in any runlevels");
- }
- # Scripts starting in rcS.d/ normally do not stop or only stop
- # during hald and shutdown.
- elsif ((array_equal(['s'], \@startrl) && array_equal([], \@stoprl))
- || ( array_equal(['s'], \@startrl)
- && array_equal(['0','6'], \@stoprl))) {
- # OK
- } else {
- # Most scripts either start in rcS.d, or in runlevels 2-5
- if (!array_equal(['2', '3', '4', '5'], \@startrl) &&
- !array_equal(['s'], \@startrl) &&
- (!array_equal([], \@startrl) && @stoprl)) {
- # Some obvious errors (runlevels 2-5 are equivalent in Debian)
- if (array_equal(['3', '5'], \@startrl)
- || array_equal(['3', '4', '5'], \@startrl)) {
- error("script $short have inconsistent start runlevels: ",
- join(" ", @startrl));
- } else {
- warning("script $short does not start in the usual runlevels: ",
- join(" ", @startrl));
- }
- }
-
- # And most scripts stop in runlevel (1) runlevels (0, 1, 6),
- # only starts or only stops in (0) or (6).
- if (!array_equal(['0', '1', '6'], \@stoprl) &&
- !array_equal(['1'], \@stoprl) &&
- !array_equal(['0', '6'], \@stoprl) &&
- !(array_equal(['0'], \@stoprl) && !@startrl) &&
- !(array_equal(['6'], \@stoprl) && !@startrl) &&
- !(array_equal([], \@stoprl) && @startrl)) {
- warning("script $short does not stop in the usual runlevels: ",
- join(" ", @stoprl));
- }
- }
- }
-
- exit 0;
-
- sub parse_lsb_header {
- my $initdscript = shift;
- my $short = short_name($initdscript);
- my %lsbinfo;
- unless (open(INIT, "<", $initdscript)) {
- error("script $short is unreadable");
- return ();
- }
- my $inheader = 0;
- while (<INIT>) {
- # print;
- chomp;
- if (m/^\#\#\# BEGIN INIT INFO\s*$/) {
- $lsbinfo{'found'} = 1;
- $inheader = 1;
- }
- $inheader = 0 if (m/\#\#\# END INIT INFO$/);
- if ($inheader
- && m/^\# ($lsbheaders|$optheaders):\s*(\S?.*)$/i) {
- # print "$1\n";
- $lsbinfo{lc($1)} = $2;
- }
- s/\#.*$//; # Remove comments
- $lsbinfo{'need_remote_fs'} = 1 if m%/usr/s?bin/%;
- $lsbinfo{'need_local_fs'} = 1 if m%/var/%;
-
- # Detect the use of tools resting in /usr/
- $lsbinfo{'need_remote_fs'} = 1 if m%awk%;
- $lsbinfo{'need_remote_fs'} = 1 if m%which%;
- }
- close(INIT);
-
- # When running on bellini.debian.org, check if $syslog is needed
- my $objdumpinfo = dirname($initdscript) . "/../objdump-info";
- if ( -f $objdumpinfo) {
- print "Checking for syslog symbol\n";
- if (open(OBJDUMP, "<", $objdumpinfo)) {
- while (<OBJDUMP>) {
- $lsbinfo{'need_syslog'} = 1 if /GLIBC.* syslog/;
- }
- close OBJDUMP;
- }
- }
-
- # Check that all the required headers are present
- if (!$lsbinfo{'found'}) {
- error("script $short is missing LSB header");
- } else {
- for my $key (split(/\|/, lc($lsbheaders))) {
- if (!exists $lsbinfo{$key}) {
- error("script $short missing LSB keyword '$key'");
- }
- }
- }
- return %lsbinfo
- }
-
- sub short_name {
- my @scripts;
- for my $script ( @_ ) {
- my $copy = $script;
- $copy =~ s%$basedir/%%g;
- push @scripts, $copy;
- }
- if (wantarray) {
- return @scripts;
- } else {
- return $scripts[0];
- }
- }
-
- sub array_equal {
- my ($a1, $a2) = @_;
- return 0 if (scalar @{$a1} != scalar @{$a2});
-
- my $i = 0;
- while ($i < scalar @{$a1}) {
- return 0 if $a1->[$i] ne $a2->[$i];
- $i++;
- }
- return 1;
- }
-
- sub info {
- print "info: @_\n";
- }
-
- sub warning {
- print "warning: @_\n" if $warn;
- }
-
- sub error {
- print "error: @_\n";
- }
-